Tree History
Scenario
As a member of the local city council's urban planning department,
I want to conduct a comprehensive analysis of the diversity of tree species within the Melbourne area, comparing the current variety and density of tree species to historical data from pre-colonial times,
So that I can identify significant changes and trends in tree diversity over time, specifically pinpointing species that have become less common or are on the verge of disappearing from the local ecosystem.
This will help in making informed decisions about which species to prioritize in future tree planting programs, aiming to restore ecological balance, enhance urban biodiversity, and support sustainable urban development.
By utilizing interactive mapping tools and temporal data visualization techniques, I hope to present this data in a clear and actionable manner to stakeholders and the public, ensuring the planning and execution of greening initiatives are well-supported by historical trends and current needs. tives are well-supported by historical trends and current needs.
Skills Demonstrated in the Tree Mapping Project
-
1. Geospatial Analysis
- Data Manipulation: Utilized Pandas and GeoPandas for transforming and preparing spatial data, crucial for mapping and analysis.
- Spatial Visualization: Employed Folium to create interactive maps that visualize geographic data, enhancing the understanding of spatial distribution and density of tree plantations.
-
2. Data Visualization
- Interactive Mapping: Implemented interactive elements in maps using Folium, including tooltips and popups that provide more detailed information dynamically.
- Temporal Data Representation:** Integrated a time slider feature to visualize the temporal progression of tree planting, which illustrates changes over time and provides insights into urban greening efforts.
-
3. Programming and Technology
- Python Programming:** Applied advanced Python programming techniques for data manipulation, analysis, and visualization, demonstrating strong coding skills.
- Use of Libraries and Plugins: Leveraged various Python libraries such as Folium, GeoPandas, and Pandas, along with plugins like MarkerCluster and Search to enhance the interactivity and functionality of the maps.
-
4. Critical Thinking and Problem Solving
- Identification and Solution Design: Identified the need for specific types of data visualizations (e.g., clustering of markers, searchable interactive maps) and implemented solutions that address these requirements effectively.
- Optimization and Performance Tuning: Optimized the map's performance and user experience by clustering data points and using GeoJSON for efficient data handling and search capabilities.
-
5. Communication and Presentation
- Clear Data Presentation: Designed maps that are not only functional but also user-friendly, ensuring that complex data is accessible and understandable to a broad audience.
- Documentation and Reporting: Crafted well-documented code and prepared comprehensive reports that explain the methodology, tools used, and insights derived from the data.
These skills are indicative of a well-rounded ability to handle, analyze, and present geospatial data in a manner that supports urban planning and environmental analysis, demonstrating both technical proficiency and analytical acumen.
Project Goal: Melbourne Tree Species Analysis for Conservation Effort
Conduct an in-depth analysis of the range of tree species in the Melbourne area, comparing historical data from pre-colonial times with current records. This study aims to identify changes in tree diversity, highlighting species that have diminished in numbers or variety. The ultimate goal is to provide evidence-based recommendations to local councils for tree planting programs, prioritizing species that need increased numbers to restore ecological balance and enhance urban biodiversity.
Key Activities
- Historical Data Collection: Gather and analyze historical records and indigenous knowledge to identify the pre-colonial tree species composition.
- Current Species Survey: Utilize current botanical surveys, remote sensing data, and council records to compile an up-to-date list of tree species within Melbourne.
- Comparative Analysis: Compare historical and current data to identify trends, losses, or gains in tree species diversity.
- Recommendation Development: Based on the analysis, recommend tree species that need conservation focus and increased numbers in planting programs.
Expected Outcome
A comprehensive report detailing changes in Melbourne's tree species over time, with actionable recommendations for local councils to inform future tree planting initiatives, aiming to restore lost tree species and enhance urban green spaces.
Impact
This project not only contributes to the conservation of Melbourne's botanical heritage but also supports urban sustainability efforts. By informing council tree planting programs with a focus on biodiversity, we can work towards a greener, more resilient Melbourne for future generations.
Datasets
Installing all necessasory packages (optional)¶
# !pip install requests pandas matplotlib-venn folium geopandas seaborn statsmodels
Importing all the necessory libraries¶
import requests
import pandas as pd
import os
from matplotlib_venn import venn2
import matplotlib.pyplot as plt
import folium
from folium.plugins import MarkerCluster, FeatureGroupSubGroup
import geopandas as gpd
import seaborn as sns
import matplotlib.pyplot as plt
from statsmodels.tsa.stattools import adfuller
from statsmodels.tsa.arima.model import ARIMA
import warnings
warnings.filterwarnings('ignore')
Importing datasets¶
def fetch_data(base_url, dataset, api_key, num_records=99, offset=0):
all_records = []
max_offset = 9900 # Maximum number of requests
while True:
# maximum limit check
if offset > max_offset:
break
# Create API request URL
filters = f'{dataset}/records?limit={num_records}&offset={offset}'
url = f'{base_url}{filters}&api_key={api_key}'
# Start request
try:
result = requests.get(url, timeout=10)
result.raise_for_status()
records = result.json().get('results')
except requests.exceptions.RequestException as e:
raise Exception(f"API request failed: {e}")
if records is None:
break
all_records.extend(records)
if len(records) < num_records:
break
# next cycle offset
offset += num_records
# DataFrame all data
df = pd.DataFrame(all_records)
return df
API_KEY = os.environ.get("API_KEY ")
# print("API Key:", API_KEY)
BASE_URL = 'https://data.melbourne.vic.gov.au/api/explore/v2.1/catalog/datasets/'
# data set name
PRE_COLONIAL_TREES = 'pre-colonial-plant-list'
pre_colonial_trees = fetch_data(BASE_URL, PRE_COLONIAL_TREES, API_KEY)
pre_colonial_trees
| species | common_name_s | certain | beach_and_dunes | saltmarsh | coastal_marshlands_and_brackish_flats | swamp_scrub | woodlands_and_heathlands_on_sand | woodlands_and_forests_on_sedimentary_hills_valleys_and_ridges | grasslands_and_woodlands_on_fertile_plains | cliffs_and_escarpments | river_banks_and_creeklines | wet_heathland | freshwater_wetland | saltwater_wetland | most_likely_occurrence | epbc_category_of_threat | ffg_extinction_risk | ffg_category_of_threat | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | Acacia mucronata var. longifolia | Narrow-leaf Wattle, Variable Sallow Wattle | NaN | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 1.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0 | 1.0 | None | None | None |
| 1 | Acacia pycnantha | Golden Wattle | 1.0 | 0.0 | 0.0 | 0.0 | 1.0 | 2.1 | 3.2 | 3.1 | 2.1 | 2.1 | 0.0 | 0.0 | 0 | 3.2 | None | None | None |
| 2 | Acacia verticillata var. verticillata | Prickly Moses | NaN | 0.0 | 0.0 | 0.0 | 3.1 | 3.1 | 0.0 | 0.0 | 0.0 | 3.1 | 1.0 | 0.0 | 0 | 3.1 | None | None | None |
| 3 | Acaena echinata | Sheep's Burr | 1.0 | 0.0 | 0.0 | 0.0 | 0.0 | 1.0 | 3.1 | 3.1 | 3.1 | 2.1 | 0.0 | 0.0 | 0 | 3.1 | None | None | None |
| 4 | Acrotriche prostrata | Trailing Ground-Berry | NaN | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 1.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0 | 1.0 | None | None | None |
| ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
| 1062 | Xanthorrhoea minor subsp. lutea | Small Grass Tree, Toolimerin | NaN | 0.0 | 0.0 | 0.0 | 0.0 | 2.1 | 1.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0 | 2.1 | None | None | None |
| 1063 | Xerochrysum bracteatum | Golden Everlasting, Straw Flower | NaN | 0.0 | 0.0 | 0.0 | 0.0 | 1.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0 | 1.0 | None | None | None |
| 1064 | Zostera muelleri subsp. capricorni | Eel-grass | NaN | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 3 | 3.2 | None | None | None |
| 1065 | Zostera muelleri subsp. muelleri | Dwarf Grass-wrack | NaN | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 2 | 2.2 | None | None | None |
| 1066 | Zygophyllum billardierei | Coast Twinleaf | NaN | 2.1 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0 | 2.1 | None | None | None |
1067 rows × 19 columns
# data set name
CURRENT_TREES = 'trees-with-species-and-dimensions-urban-forest'
current_trees = fetch_data(BASE_URL, CURRENT_TREES, API_KEY)
current_trees
| com_id | common_name | scientific_name | genus | family | diameter_breast_height | year_planted | date_planted | age_description | useful_life_expectency | useful_life_expectency_value | precinct | located_in | uploaddate | coordinatelocation | latitude | longitude | easting | northing | geolocation | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | 1546404 | River Sheoak | Casuarina cunninghamiana | Casuarina | Casuarinaceae | NaN | 2013 | 2013-01-07 | None | None | NaN | None | Park | 2021-01-10 | {'lon': 144.97177997020128, 'lat': -37.8180699... | -37.818070 | 144.971780 | 321479.34 | 5812432.31 | {'lon': 144.97177997020128, 'lat': -37.8180699... |
| 1 | 1038666 | Claret Ash | Fraxinus angustifolia subsp. oxycarpa | Fraxinus | Oleaceae | 26.0 | 1998 | 1998-11-09 | Semi-Mature | 11-20 years | 20.0 | None | Park | 2021-01-10 | {'lon': 144.96069216093832, 'lat': -37.7815772... | -37.781577 | 144.960692 | 320414.93 | 5816460.51 | {'lon': 144.96069216093832, 'lat': -37.7815772... |
| 2 | 1439845 | Kanooka | Tristaniopsis laurina | Tristaniopsis | Myrtaceae | NaN | 2009 | 2009-09-08 | None | None | NaN | None | Street | 2021-01-10 | {'lon': 144.9455194257097, 'lat': -37.79259845... | -37.792598 | 144.945519 | 319105.53 | 5815208.27 | {'lon': 144.9455194257097, 'lat': -37.79259845... |
| 3 | 1070962 | Spotted Gum | Corymbia maculata | Corymbia | Myrtaceae | 17.0 | 2007 | 2007-06-14 | Semi-Mature | 31-60 years | 60.0 | None | Street | 2021-01-10 | {'lon': 144.9421772126995, 'lat': -37.78013819... | -37.780138 | 144.942177 | 318780.78 | 5816584.46 | {'lon': 144.9421772126995, 'lat': -37.78013819... |
| 4 | 1066252 | River red gum | Eucalyptus camaldulensis | Eucalyptus | Myrtaceae | 19.0 | 2006 | 2006-02-20 | Semi-Mature | 61+ years | 80.0 | None | Street | 2021-01-10 | {'lon': 144.92039340703647, 'lat': -37.7905765... | -37.790577 | 144.920393 | 316887.98 | 5815383.70 | {'lon': 144.92039340703647, 'lat': -37.7905765... |
| ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
| 9994 | 1066718 | Yellow Box | Eucalyptus melliodora | Eucalyptus | Myrtaceae | NaN | 2006 | 2006-09-14 | None | None | NaN | None | Park | 2021-01-10 | {'lon': 144.9546546149572, 'lat': -37.78488602... | -37.784886 | 144.954655 | 319891.23 | 5816081.73 | {'lon': 144.9546546149572, 'lat': -37.78488602... |
| 9995 | 1735402 | Manuka | Kunzea ericoides | Kunzea | Myrtaceae | NaN | 2018 | 2018-05-30 | None | None | NaN | None | Park | 2021-01-10 | {'lon': 144.9514066985289, 'lat': -37.79316880... | -37.793169 | 144.951407 | 319625.34 | 5815156.36 | {'lon': 144.9514066985289, 'lat': -37.79316880... |
| 9996 | 1656707 | Norfolk Island Pine | Araucaria heterophylla | Araucaria | Araucariaceae | NaN | 2017 | 2017-04-26 | None | None | NaN | None | Street | 2021-01-10 | {'lon': 144.9471755893213, 'lat': -37.81988993... | -37.819890 | 144.947176 | 319317.91 | 5812183.04 | {'lon': 144.9471755893213, 'lat': -37.81988993... |
| 9997 | 1604462 | Lightwood Wattle | Acacia implexa | Acacia | Fabaceae | NaN | 2015 | 2015-08-25 | None | None | NaN | None | Park | 2021-01-10 | {'lon': 144.9524311587096, 'lat': -37.79529060... | -37.795291 | 144.952431 | 319720.71 | 5814922.89 | {'lon': 144.9524311587096, 'lat': -37.79529060... |
| 9998 | 1049080 | River red gum | Eucalyptus camaldulensis | Eucalyptus | Myrtaceae | NaN | 1998 | 1998-12-16 | None | None | NaN | None | Park | 2021-01-10 | {'lon': 144.94552257177912, 'lat': -37.7903449... | -37.790345 | 144.945523 | 319100.31 | 5815458.34 | {'lon': 144.94552257177912, 'lat': -37.7903449... |
9999 rows × 20 columns
Preprocess the Data¶
Displaying Available Columns¶
# Print available columns in the pre-colonial trees cleaned dataset
print("Available columns in pre-colonial trees dataset:")
print(pre_colonial_trees.columns.tolist())
# Print available columns in the current trees cleaned dataset
print("\nAvailable columns in current trees dataset:")
print(current_trees.columns.tolist())
Available columns in pre-colonial trees dataset: ['species', 'common_name_s', 'certain', 'beach_and_dunes', 'saltmarsh', 'coastal_marshlands_and_brackish_flats', 'swamp_scrub', 'woodlands_and_heathlands_on_sand', 'woodlands_and_forests_on_sedimentary_hills_valleys_and_ridges', 'grasslands_and_woodlands_on_fertile_plains', 'cliffs_and_escarpments', 'river_banks_and_creeklines', 'wet_heathland', 'freshwater_wetland', 'saltwater_wetland', 'most_likely_occurrence', 'epbc_category_of_threat', 'ffg_extinction_risk', 'ffg_category_of_threat'] Available columns in current trees dataset: ['com_id', 'common_name', 'scientific_name', 'genus', 'family', 'diameter_breast_height', 'year_planted', 'date_planted', 'age_description', 'useful_life_expectency', 'useful_life_expectency_value', 'precinct', 'located_in', 'uploaddate', 'coordinatelocation', 'latitude', 'longitude', 'easting', 'northing', 'geolocation']
Removing unwanted columns¶
# Columns to remove from the pre-colonial dataset
columns_to_remove_pre_colonial = ['epbc_category_of_threat', 'ffg_extinction_risk', 'ffg_category_of_threat', 'certain']
# Removing the specified columns from the pre-colonial trees dataset
pre_colonial_trees_cleaned = pre_colonial_trees.drop(columns=columns_to_remove_pre_colonial)
# Print available columns in the pre-colonial trees cleaned dataset after removal
print("Available columns in pre-colonial trees dataset after removal:")
print(pre_colonial_trees_cleaned.columns.tolist())
# Columns to remove from the current trees dataset
columns_to_remove_current = ['age_description', 'useful_life_expectency', 'useful_life_expectency_value', 'precinct', 'diameter_breast_height', 'coordinatelocation', 'easting', 'northing', 'geolocation']
# Removing the specified columns from the current trees dataset
current_trees_cleaned = current_trees.drop(columns=columns_to_remove_current)
# Print available columns in the current trees cleaned dataset after removal
print("\nAvailable columns in current trees dataset after removal:")
print(current_trees_cleaned.columns.tolist())
Available columns in pre-colonial trees dataset after removal: ['species', 'common_name_s', 'beach_and_dunes', 'saltmarsh', 'coastal_marshlands_and_brackish_flats', 'swamp_scrub', 'woodlands_and_heathlands_on_sand', 'woodlands_and_forests_on_sedimentary_hills_valleys_and_ridges', 'grasslands_and_woodlands_on_fertile_plains', 'cliffs_and_escarpments', 'river_banks_and_creeklines', 'wet_heathland', 'freshwater_wetland', 'saltwater_wetland', 'most_likely_occurrence'] Available columns in current trees dataset after removal: ['com_id', 'common_name', 'scientific_name', 'genus', 'family', 'year_planted', 'date_planted', 'located_in', 'uploaddate', 'latitude', 'longitude']
Printing Available Columns after Removing Unwanted Columns¶
# Print available columns in the pre-colonial trees cleaned dataset
print("Available columns in pre-colonial trees dataset:")
print(pre_colonial_trees_cleaned.columns.tolist())
# Print available columns in the current trees cleaned dataset
print("\nAvailable columns in current trees dataset:")
print(current_trees_cleaned.columns.tolist())
Available columns in pre-colonial trees dataset: ['species', 'common_name_s', 'beach_and_dunes', 'saltmarsh', 'coastal_marshlands_and_brackish_flats', 'swamp_scrub', 'woodlands_and_heathlands_on_sand', 'woodlands_and_forests_on_sedimentary_hills_valleys_and_ridges', 'grasslands_and_woodlands_on_fertile_plains', 'cliffs_and_escarpments', 'river_banks_and_creeklines', 'wet_heathland', 'freshwater_wetland', 'saltwater_wetland', 'most_likely_occurrence'] Available columns in current trees dataset: ['com_id', 'common_name', 'scientific_name', 'genus', 'family', 'year_planted', 'date_planted', 'located_in', 'uploaddate', 'latitude', 'longitude']
Renaming Columns to Match Datasets¶
# Rename "Scientific Name" column to "Species" in the current trees dataset
current_trees_renamed = current_trees_cleaned.rename(columns={"scientific_name": "species"})
# Verify the column renaming by printing the columns of the updated DataFrame
print("Available columns in current trees dataset after renaming:")
print(current_trees_renamed.columns.tolist())
Available columns in current trees dataset after renaming: ['com_id', 'common_name', 'species', 'genus', 'family', 'year_planted', 'date_planted', 'located_in', 'uploaddate', 'latitude', 'longitude']
Analysis & Visualization¶
current_trees_renamed
| com_id | common_name | species | genus | family | year_planted | date_planted | located_in | uploaddate | latitude | longitude | |
|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | 1546404 | River Sheoak | Casuarina cunninghamiana | Casuarina | Casuarinaceae | 2013 | 2013-01-07 | Park | 2021-01-10 | -37.818070 | 144.971780 |
| 1 | 1038666 | Claret Ash | Fraxinus angustifolia subsp. oxycarpa | Fraxinus | Oleaceae | 1998 | 1998-11-09 | Park | 2021-01-10 | -37.781577 | 144.960692 |
| 2 | 1439845 | Kanooka | Tristaniopsis laurina | Tristaniopsis | Myrtaceae | 2009 | 2009-09-08 | Street | 2021-01-10 | -37.792598 | 144.945519 |
| 3 | 1070962 | Spotted Gum | Corymbia maculata | Corymbia | Myrtaceae | 2007 | 2007-06-14 | Street | 2021-01-10 | -37.780138 | 144.942177 |
| 4 | 1066252 | River red gum | Eucalyptus camaldulensis | Eucalyptus | Myrtaceae | 2006 | 2006-02-20 | Street | 2021-01-10 | -37.790577 | 144.920393 |
| ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
| 9994 | 1066718 | Yellow Box | Eucalyptus melliodora | Eucalyptus | Myrtaceae | 2006 | 2006-09-14 | Park | 2021-01-10 | -37.784886 | 144.954655 |
| 9995 | 1735402 | Manuka | Kunzea ericoides | Kunzea | Myrtaceae | 2018 | 2018-05-30 | Park | 2021-01-10 | -37.793169 | 144.951407 |
| 9996 | 1656707 | Norfolk Island Pine | Araucaria heterophylla | Araucaria | Araucariaceae | 2017 | 2017-04-26 | Street | 2021-01-10 | -37.819890 | 144.947176 |
| 9997 | 1604462 | Lightwood Wattle | Acacia implexa | Acacia | Fabaceae | 2015 | 2015-08-25 | Park | 2021-01-10 | -37.795291 | 144.952431 |
| 9998 | 1049080 | River red gum | Eucalyptus camaldulensis | Eucalyptus | Myrtaceae | 1998 | 1998-12-16 | Park | 2021-01-10 | -37.790345 | 144.945523 |
9999 rows × 11 columns
1. Historical vs. Current Species Diversity (Venn Diagram)¶
# Extracting species lists from both datasets
pre_colonial_species = set(pre_colonial_trees_cleaned['species'].unique())
current_species = set(current_trees_renamed['species'].unique())
# Creating the Venn diagram
plt.figure(figsize=(10, 7))
venn2([pre_colonial_species, current_species], ('Pre-Colonial Trees', 'Current Trees'))
plt.title('Comparison of Tree Species Diversity: Pre-Colonial vs Current')
plt.show()
Historical vs. Current Species Diversity (Venn Diagram)¶
This Venn diagram visually compares the diversity of tree species from pre-colonial times to the present day within Melbourne. The diagram's left circle represents species recorded historically, while the right circle shows species currently found in urban areas.
Insights:
- Unique Historical Species: The left exclusive area (Count of 1051) indicates species that were present historically but are no longer found, highlighting potential extinctions or migrations.
- Unique Current Species: The right exclusive area (Count of 327) shows species that are newly recorded, reflecting introductions or recent naturalizations in the urban environment.
- Shared Species: The intersection (Count of 16) represents species that have persisted through time, indicating resilience or stable ecological niches.
This analysis helps identify priority species for conservation efforts and guides future tree planting initiatives to enhance biodiversity and ecological stability in Melbourne's urban landscape.
2. Change in Species Abundance Over Time¶
# Extract species sets
pre_colonial_species = set(pre_colonial_trees_cleaned['species'].unique())
current_species = set(current_trees_renamed['species'].unique())
# Determine shared and unique species
shared_species = pre_colonial_species.intersection(current_species)
lost_species = pre_colonial_species.difference(current_species)
gained_species = current_species.difference(pre_colonial_species)
# Print results
print(f"Shared Species: {len(shared_species)}")
print(f"Lost Species: {len(lost_species)}")
print(f"Gained Species: {len(gained_species)}")
Shared Species: 16 Lost Species: 1051 Gained Species: 327
Change in Species Abundance Over Time¶
This section analyzes the shifts in species abundance over specified time periods, identifying trends in species gains and losses. This information is crucial for assessing the impacts of urban development and environmental changes on local biodiversity.
Insights:
- Species Gained: 327 species were gained over time, possibly due to new planting policies or ecological changes.
- Species Lost: 1051 species were lost over toime, which might indicate negative impacts like habitat loss or climate change.
- Species Shared: 16 species were found to be exsisting over time.
Understanding these trends allows city planners and conservationists to make informed decisions on which tree species need protective measures and which might be suitable for future planting.
3. Geographical Distribution of Trees (Map Visualization)¶
# Convert DataFrame to GeoDataFrame
gdf = gpd.GeoDataFrame(
current_trees_renamed,
geometry=gpd.points_from_xy(current_trees_renamed.longitude, current_trees_renamed.latitude),
crs="EPSG:4326"
)
# Initialize a map centered around Melbourne
melbourne_map = folium.Map(location=[-37.8136, 144.9631], zoom_start=12)
# Use MarkerCluster to manage multiple markers
marker_cluster = MarkerCluster().add_to(melbourne_map)
# Create FeatureGroupSubGroups for parks and streets within the MarkerCluster
park_group = FeatureGroupSubGroup(marker_cluster, 'Parks')
street_group = FeatureGroupSubGroup(marker_cluster, 'Streets')
park_group.add_to(melbourne_map)
street_group.add_to(melbourne_map)
# Add markers to the appropriate subgroup based on their location
for idx, row in gdf.iterrows():
marker = folium.CircleMarker(
location=[row.geometry.y, row.geometry.x],
radius=3,
color='green' if row['located_in'] == 'Park' else 'red',
fill=True,
fill_color='green' if row['located_in'] == 'Park' else 'red',
popup=f"Species: {row['species']}, Planted: {row['date_planted']}"
)
if row['located_in'] == 'Park':
marker.add_to(park_group)
else:
marker.add_to(street_group)
# Add LayerControl to toggle parks and streets
folium.LayerControl().add_to(melbourne_map)
# Display the map
melbourne_map